package xginplus

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"gitee.com/xiaoyutab/xgotool/xstring"
	"github.com/gin-gonic/gin/binding"
)

// 获取请求中的参数
//
//	b	待获取的参数结构体信息
func (c *Context) Bind(b any) error {
	if c.Request.Method == "GET" {
		return c.ShouldBindWith(b, binding.Query)
	}
	switch c.ContentType() {
	case "application/json":
		return c.ShouldBindBodyWith(b, binding.JSON)
	case "application/xml":
		return c.ShouldBindBodyWith(b, binding.XML)
	default:
		return c.ShouldBindWith(b, binding.Form)
	}
}

// 使用反射的形式进行参数绑定（目前仅限json形式绑定）
//
//	b	待获取的参数结构体信息
func (c *Context) RBind(b any) error {
	if c.Request.Method == "GET" {
		return c.ShouldBindWith(b, binding.Query)
	}
	switch c.ContentType() {
	case "application/json":
		body, err := c.GetRequestBody()
		if err != nil {
			return err
		}
		// 进行绑定解析
		return Bind(body, b)
	case "application/xml":
		return c.ShouldBindBodyWith(b, binding.XML)
	default:
		return c.ShouldBindWith(b, binding.Form)
	}
}

// 绑定结构体数据
// 该绑定类型处理较为粗糙，且为了易用而牺牲了性能，请慎重处理
//
// bool: 非空字符串(考虑到前端转换的兼容性，"false"和"0"会视为false的值)、非0数字、非false值均视为true
// string: 字符串原样返回，数字转为字符串，布尔值转为字符串true/false
// int等: 数字原样返回，字符串会尝试转换为数字，布尔值视为1/0
// float32等: 数字原样返回，字符串会尝试转换为数字，布尔值视为1.0/0.0
//
//	b	待绑定的数据（JSON字节码）
//	v	待绑定的结构体变量
func Bind(b []byte, v any) error {
	// 获取v的反射结构信息
	rt := reflect.TypeOf(v)
	// 如果是指针类型，则获取指针指向的类型
	if rt.Kind() == reflect.Ptr {
		if rt.Elem().Kind() == reflect.Slice {
			return bindList(b, v)
		}
		rt = rt.Elem()
	}
	mps := map[string]any{}
	err := json.Unmarshal(b, &mps)
	if err != nil {
		// 暂时不考虑列表
		return err
	}
	// 遍历结构体字段
	for i := 0; i < rt.NumField(); i++ {
		name := rt.Field(i).Tag.Get("json")
		if strings.Contains(name, ",") {
			name = name[:strings.Index(name, ",")]
		}
		if name == "" {
			name = rt.Field(i).Name
		}
		// 判断该类型是字符串、整形、浮点型等基础类型
		switch rt.Field(i).Type.Kind() {
		case reflect.Bool:
			if raw, ok := mps[name]; ok {
				reflect.ValueOf(v).Elem().Field(i).SetBool(xstring.ABool(raw))
			}
		case reflect.String:
			if raw, ok := mps[name]; ok {
				reflect.ValueOf(v).Elem().Field(i).SetString(xstring.AString(raw))
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if raw, ok := mps[name]; ok {
				reflect.ValueOf(v).Elem().Field(i).SetInt(xstring.Any2Integer(raw))
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			if raw, ok := mps[name]; ok {
				reflect.ValueOf(v).Elem().Field(i).SetUint(xstring.Any2Uint64(raw))
			}
		case reflect.Float32, reflect.Float64:
			if raw, ok := mps[name]; ok {
				reflect.ValueOf(v).Elem().Field(i).SetFloat(xstring.Any2Float(raw))
			}
		case reflect.Struct:
			if raw, ok := mps[name]; ok {
				b, _ := json.Marshal(raw)
				err_tmp := Bind(b, reflect.ValueOf(v).Elem().Field(i).Addr().Interface())
				if err_tmp != nil {
					err = fmt.Errorf("%s.%s binded error: %s", rt.Field(i).Name, name, err_tmp)
				}
			}
		case reflect.Ptr:
			if raw, ok := mps[name]; ok {
				b, _ := json.Marshal(raw)
				if reflect.ValueOf(v).Elem().Field(i).IsNil() {
					reflect.ValueOf(v).Elem().Field(i).Set(reflect.New(rt.Field(i).Type.Elem()))
				}
				err_tmp := Bind(b, reflect.ValueOf(v).Elem().Field(i).Interface())
				if err_tmp != nil {
					err = fmt.Errorf("%s.%s binded error: %s", rt.Field(i).Name, name, err_tmp)
				}
			}
		case reflect.Slice:
			if raw, ok := mps[name]; ok {
				b, _ := json.Marshal(raw)
				if reflect.ValueOf(v).Elem().Field(i).IsNil() {
					reflect.ValueOf(v).Elem().Field(i).Set(reflect.MakeSlice(rt.Field(i).Type, 0, 0))
				}
				err_tmp := bindList(b, reflect.ValueOf(v).Elem().Field(i).Addr().Interface())
				if err_tmp != nil {
					err = fmt.Errorf("%s.%s binded error: %s", rt.Field(i).Name, name, err_tmp)
				}
			}
		case reflect.Interface:
			var tmp any
			if err := json.Unmarshal(b, &tmp); err != nil {
				return fmt.Errorf("%s.%s binded error: %s", rt.Field(i).Name, name, err)
			}
			reflect.ValueOf(v).Elem().Field(i).Set(reflect.ValueOf(tmp))
		default:
			return fmt.Errorf("Bind error: unsupported type %s", rt.Field(i).Type.Kind().String())
		}
	}
	return err
}

// 绑定列表数据
//
//	b	待绑定的数据（JSON字节码）
//	v	待绑定的结构体变量
func bindList(b []byte, v any) error {
	lis := []any{}
	err := json.Unmarshal(b, &lis)
	if err != nil {
		return err
	}
	// 获取v的反射结构信息
	rt := reflect.TypeOf(v)
	// 如果是指针类型，则获取指针指向的类型
	if rt.Kind() == reflect.Ptr {
		rt = rt.Elem()
	}
	// 遍历列表数据
	for _, item := range lis {
		// 判断列表内变量是否为指针变量
		// 获取对应的结构体类型
		rt2 := reflect.New(rt.Elem()).Elem()
		rt3 := rt2
		// 获取内部数据类型
		if rt2.Kind() == reflect.Ptr {
			if rt2.IsNil() {
				rt3.Set(reflect.New(rt3.Type().Elem()))
				rt2 = rt3
			}
			rt3 = rt2.Elem()
		}
		b, _ := json.Marshal(item)
		switch rt3.Kind() {
		case reflect.Struct:
			err_tmp := Bind(b, rt3.Addr().Interface())
			if err_tmp != nil {
				err = fmt.Errorf("BindList error: %v", err_tmp)
			}
		case reflect.Slice:
			err_tmp := bindList(b, rt3.Addr().Interface())
			if err_tmp != nil {
				err = fmt.Errorf("BindList error: %v", err_tmp)
			}
		case reflect.Interface:
			var err_tmp any
			if err := json.Unmarshal(b, &err_tmp); err != nil {
				return fmt.Errorf("BindList error: %v", err)
			}
			rt3.Set(reflect.ValueOf(err_tmp))
		case reflect.String:
			rt3.SetString(xstring.AString(item))
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			rt3.SetInt(xstring.Any2Integer(item))
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			rt3.SetUint(xstring.Any2Uint64(item))
		case reflect.Float32, reflect.Float64:
			rt3.SetFloat(xstring.Any2Float(item))
		case reflect.Bool:
			rt3.SetBool(xstring.ABool(item))
		default:
			return fmt.Errorf("BindList error: unsupported type %s", rt3.Kind().String())
		}
		// 往原数据中写入数据
		reflect.ValueOf(v).Elem().Set(reflect.Append(reflect.ValueOf(v).Elem(), rt2))
	}
	return err
}
